﻿/********************************************************************************/
/*  gamecore.d																	*/
/*------------------------------------------------------------------------------*/
/*	製作		（　゜ワ゜）ノ　／　松久貫太									*/
/*	製作開始　	2007/08/26														*/
/*	MAIL		omega@personal.email.ne.jp										*/
/*	URL		http://nagoya.cool.ne.jp/o_mega										*/
/*																				*/
/*	このソースは「やらわかライセンス」の元で配布されています。					*/
/*-更新履歴---------------------------------------------------------------------*/
/*	2007/--/--																	*/
/*-その他-----------------------------------------------------------------------*/
/*	なし																		*/
/********************************************************************************/

/* ライブラリのインポート *******************************************************/
private import hell2;		// dHell2ライブラリ
private import state;
private import screen;

private import vector;
private import defines;
private import keypad;

static const int PLAYER_MAX = 2;
static const double GRID_SIZE = 3.0;
static const double GRID_BORDER = 1.0;
static const int MAPSIZE_MAX = 4;

enum CHIPTYPE{
	NONE,
	CIRCLE,
	CROSS,
	MAX,
}

// キャラクタ色
const double[] player_color_r = [0 , 1.0 , 0.1];
const double[] player_color_g = [0 , 0.5 , 0.5];
const double[] player_color_b = [0 , 0.1 , 1.0];

double toGlid(int t){return (GRID_BORDER + GRID_SIZE) * (-3 + t * 2);}

class Player{
	const int RECOVER_WAIT = 60;		// 回復までのタイムラグ/フレーム
	const int QUICK_RECOVER_WAIT = 180;	// 高速回復までの追加タイムラグ
	
	const int ENERGY_MAX = 500;			// エネルギー最大値
	const int MOVING_WAIT = 10;			// 移動ウエイト
	const double MOVING_SLIDE = 2.0;	// 体当たり失敗時のスライド幅
	
	// 行動ごとのエネルギー消費量
	const int ENERGY_MOVE = 00;
	const int ENERGY_SET = 50;
	const int ENERGY_PUSHOUT = 20;//100;
	const int ENERGY_REVERSE = 100;
	
	int energy;
	int recover_timer;
	int timer;
	int scef_timer;
	
	CHIPTYPE chiptype;
	
	int x,y;			// cursor実座標
	Vec3 cursor;		// cursor表示座標
	int tgt_x,tgt_y;	// 移動先ベクトル
	int moving_wait;	// 移動開始ラグ
	int score;			// スコア
	
	GameCore gamecore;
	Keypad keypad;
	
	this(CHIPTYPE type , GameCore gamecore , Keypad keypad){
		this.energy = ENERGY_MAX;
		this.chiptype = type;
		this.gamecore = gamecore;
		this.keypad = keypad;
		
		if(chiptype == CHIPTYPE.CIRCLE){
			x = 0;
			y = 0;
		}else if(chiptype == CHIPTYPE.CROSS){
			x = 3;
			y = 3;
		}else{
			x = 1;
			y = 1;
		}
		
		scef_timer = 0;
		cursor = new Vec3(toGlid(x) , toGlid(y) , 0);
	}
	
	void move(){
		timer++;
		if(scef_timer > 0) scef_timer--;
		
		// 移動---------------------
		bool isActioned = false;
		if(keypad.isPushDown() && moving_wait == 0 && energy > ENERGY_MOVE){
			if(movePushOut(0,1)){
				y++;
				energy -= ENERGY_MOVE;
				isActioned = true;
			}else{
				cursor.y += MOVING_SLIDE;
				isActioned = true;
			}
		}
		if(keypad.isPushUp() && moving_wait == 0 && energy > ENERGY_MOVE){
			if(movePushOut(0,-1)){
				y--;
				energy -= ENERGY_MOVE;
				isActioned = true;
			}else{
				cursor.y -= MOVING_SLIDE;
				isActioned = true;
			}
		}
		if(keypad.isPushLeft() && moving_wait == 0 && energy > ENERGY_MOVE){
			if(movePushOut(-1,0)){
				x--;
				energy -= ENERGY_MOVE;
				isActioned = true;
			}else{
				cursor.x -= MOVING_SLIDE;
				isActioned = true;
			}
		}
		if(keypad.isPushRight() && moving_wait == 0 && energy > ENERGY_MOVE){
			if(movePushOut(1,0)){
				x++;
				energy -= ENERGY_MOVE;
				isActioned = true;
			}else{
				cursor.x += MOVING_SLIDE;
				isActioned = true;
			}
		}
		x = (x + MAPSIZE_MAX) % MAPSIZE_MAX;
		y = (y + MAPSIZE_MAX) % MAPSIZE_MAX;
		cursor.x = cursor.x * 0.8 + toGlid(x) * 0.2;
		cursor.y = cursor.y * 0.8 + toGlid(y) * 0.2;
		
		if(isActioned){
			moving_wait = MOVING_WAIT;
			//recover_timer = 0;
		}
		
		if(moving_wait > 0) moving_wait--;
		
		// リカバー回復
		recover_timer++;
		if(recover_timer > RECOVER_WAIT){
			if(recover_timer > RECOVER_WAIT + QUICK_RECOVER_WAIT){
				energy += 3;	// 高速回復
			}else{
				energy++;	// 通常回復
			}
			if(energy > ENERGY_MAX) energy = ENERGY_MAX;
		}
		
		if(gamecore.gamestate == GAMESTATE.PLAYING){
			// 設置-----------------------
			if(keypad.isPushEnter() && gamecore.map.isMapChip(x,y,CHIPTYPE.NONE)
				&& moving_wait == 0 && energy > ENERGY_SET)
			{
				gamecore.map.setMapChip(x,y,chiptype);
				energy -= ENERGY_SET;
				recover_timer = 0;
			}
			// 反転
			if(keypad.isPushEnter() && !gamecore.map.isMapChip(x,y,CHIPTYPE.NONE)
				&& !gamecore.map.isMapChip(x,y,chiptype)
				&& moving_wait == 0 && energy > ENERGY_REVERSE)
			{
				gamecore.map.setMapChip(x,y,chiptype);
				energy -= ENERGY_REVERSE;
				recover_timer = 0;
			}
		}
	}
	
	void draw()
	{
		double darker = 1.0;
		if(moving_wait != 0) darker = 0.8;
		
		glPushMatrix();
		{
			glTranslatef(cursor.x , cursor.y , CHAR_Z_SURFACE + GRID_SIZE);
			glScalef(GRID_SIZE , GRID_SIZE , GRID_SIZE);
			if(timer % 10 < 9){
				glColor4f(player_color_r[cast(int)chiptype] * darker,
						player_color_g[cast(int)chiptype] * darker ,
						player_color_b[cast(int)chiptype] * darker , 1);
			}else{
				glColor4f( darker, darker ,darker , 1);
			}
			CubeShape.drawWire();
			}
		glPopMatrix();
		
		for(int t=0;t<60;t+=20)
		{
			glPushMatrix();
			{
				glTranslatef(cursor.x , cursor.y , CHAR_Z_SURFACE + GRID_SIZE);
				double sz = GRID_SIZE * ( 1 + ((timer + t) % 30) / 60.0);
				glScalef(sz,sz,sz);
				glColor4f(player_color_r[cast(int)chiptype] * darker ,
						player_color_g[cast(int)chiptype] * darker ,
						player_color_b[cast(int)chiptype] * darker ,
						0.8 - ((timer + t) % 30) / 30.0);
				CubeShape.drawWire();
			}
			glPopMatrix();
		}
	}
	
	// 状態表示
	void drawStatus(){
		switch(chiptype){
			case CHIPTYPE.CIRCLE:
				Hell_drawFont("EN:" ~ std.string.toString(energy) , 0 , 64 , 2);
				Hell_drawFont("SC:" ~ std.string.toString(score) , 0 , 64 + 16 , 2 + scef_timer / 15.0);
				
				Hell_drawFont("RST:" ~ std.string.toString(gamecore.goalscore - score) , 0 , 64 + 32 , 2);
				break;
			case CHIPTYPE.CROSS:
				Hell_drawFont("EN:" ~ std.string.toString(energy) , 520 , 64 , 2);
				Hell_drawFont("SC:" ~ std.string.toString(score) , 520 , 64 + 16 , 2 + scef_timer / 15.0);
				
				Hell_drawFont("RST:" ~ std.string.toString(gamecore.goalscore - score) , 520 , 64 + 32 , 2);
				break;
			default:
				break;
		}
	}
	
	// 移動時の衝突処理/押し出し
	bool movePushOut(int vx,int vy){
		int tgx = (x + vx + MAPSIZE_MAX) % MAPSIZE_MAX;
		int tgy = (y + vy + MAPSIZE_MAX) % MAPSIZE_MAX;
		bool result = true;
		foreach(inout p;gamecore.player){
			if(p == this) continue;
			
			if(p.x == tgx && p.y == tgy){
				if(energy >= ENERGY_PUSHOUT){
					p.x = (p.x + vx + MAPSIZE_MAX) % MAPSIZE_MAX;
					p.y = (p.y + vy + MAPSIZE_MAX) % MAPSIZE_MAX;
					energy -= ENERGY_PUSHOUT;
					result = true;
				}else{
					energy = 0;
					result = false;
				}
			}
		}
		
		return result;
	}
	
	void addScore(int score){
		scef_timer = 15;
		this.score += score;
	}
}

const int[] VEC_X = [0,1,1,-1];	// 判定用ベクトルデータ
const int[] VEC_Y = [1,0,1,1];

class Map{
	MapChip[] mapchip;
	int timer;
	
	const int LINE3_SCORE = 100;	// 3個ライン得点
	
	GameCore gamecore;
	
	this(GameCore gamecore){
		this.gamecore = gamecore;
		mapchip = new MapChip[MAPSIZE_MAX * MAPSIZE_MAX];
		timer = 0;
		
		for(int t=0;t<MAPSIZE_MAX * MAPSIZE_MAX;t++){
			mapchip[t] = new MapChip();
		}
	}
	
	void setMapChip(int x,int y,CHIPTYPE chiptype){
		mapchip[toXY(x,y)].life = MapChip.LIFE_MAX;
		mapchip[toXY(x,y)].chiptype = chiptype;
		mapchip[toXY(x,y)].history[cast(int)chiptype]++;
	}
	bool isMapChip(int x,int y,CHIPTYPE chiptype){
		if(mapchip[toXY(x,y)].chiptype == chiptype) return true;
		return false;
	}
	
	void move(){
		timer++;
		for(int t=0;t<MAPSIZE_MAX * MAPSIZE_MAX;t++){
			int x = t % MAPSIZE_MAX;
			int y = t / MAPSIZE_MAX;
			mapchip[t].move();
		}
		
		//if(timer % 60 == 0)
		checkLines();
	}
	
	void checkLines(){
		// 初期化
		for(int t=0;t<MAPSIZE_MAX * MAPSIZE_MAX;t++){
			if(mapchip[t].chiptype == CHIPTYPE.NONE) continue;
			CHIPTYPE sametype = mapchip[t].chiptype;
			
			for(int e=0;e<VEC_X.length;e++){
				if(!mapchip[t].isCheckable(e)) continue;
				
				// 3個連続を検索
				bool satisfy = true;
				int vx = toX(t) , vy = toY(t);
				
				for(int rep = 0 ; rep < 2 ;rep++){
					vx += VEC_X[e];
					vy += VEC_Y[e];
					if(inMap(vx,vy)){
						if(mapchip[toXY(vx,vy)].chiptype != sametype) satisfy = false;
					}else{
						satisfy = false;
					}
				}
				
				// 条件成立
				if(satisfy == true){
					mapchip[t].setCheckFlag(e);
					foreach(inout p;gamecore.player){
						if(p.chiptype == sametype) p.addScore(LINE3_SCORE);
					}
					
					vx = toX(t);
					vy = toY(t);
					for(int rep = 0 ; rep < 3 ;rep++){
						vx += VEC_X[e];
						vy += VEC_Y[e];
					//	mapchip[toXY(vx,vy)].isFlash = true;
					}
				}
			}
		}
	}
	
	void draw(){
		setDepthTest();
		for(int t=0;t<MAPSIZE_MAX * MAPSIZE_MAX;t++){
			glPushMatrix();
			{
				glTranslatef(toGlid(toX(t)) , toGlid(toY(t)), CHAR_Z_SURFACE - GRID_SIZE);
				glScalef(GRID_SIZE , GRID_SIZE , GRID_SIZE);
				glColor4f(0.3 , 0.3 , 0.3 , 1);
				CubeShape.drawPolygon();
				glColor4f(0,0,0,1);
				CubeShape.drawWire();
			}
			glPopMatrix();
		}
		resetDepthTest();
		
		for(int t=0;t<MAPSIZE_MAX * MAPSIZE_MAX;t++){
			glPushMatrix();
			{
				glTranslatef(toGlid(toX(t)) , toGlid(toY(t)), CHAR_Z_SURFACE - GRID_SIZE);
				mapchip[t].drawBox();
			}
			glPopMatrix();
		}
		
		Hell_setAlpha(HELL_ALPHA_ADD);
		for(int t=0;t<MAPSIZE_MAX * MAPSIZE_MAX;t++){
			glPushMatrix();
			{
				glTranslatef(toGlid(toX(t)) , toGlid(toY(t)), CHAR_Z_SURFACE - GRID_SIZE);
				mapchip[t].drawMark();
			}
			glPopMatrix();
		}
		Hell_setAlpha(HELL_ALPHA_NORMAL);
	}
	
	void drawStatus(){
		Hell_setAlpha(HELL_ALPHA_NORMAL);
		Hell_drawFont(std.string.toString((timer / 6) % 10) , 300 , 32 , 4);
	}
	
	int toX(int t){return t % MAPSIZE_MAX;}
	int toY(int t){return t / MAPSIZE_MAX;}
	int toXY(int x,int y){return x + y * MAPSIZE_MAX;}
	bool inMap(int x,int y){
		if(x < 0 || x >= MAPSIZE_MAX || y < 0 || y >= MAPSIZE_MAX) return false;
		return true;
	}
}

class MapChip{
	const double LIFE_MAX = 1.0;
	const double LIFE_TIMEDEC = 0.0012;
	const double LIFE_FLASH_DECRATIO = 2;
	const double LIFE_HISTORY_DECRATIO = 2;
	
	const int CHECK_INTERVAL = 60;
	
	CHIPTYPE chiptype;
	
	double life;
	double history[CHIPTYPE.MAX];
	int timer;
	
	int check_timer[];		// 判定用タイマ
	
	this()
	{
		life = 0;
		chiptype = CHIPTYPE.NONE;
		timer = 0;
		
		check_timer = new int[VEC_X.length];
		foreach(inout t;check_timer) t = 0;
		
		foreach(inout t;history) t = 0;
	}
	
	void setCheckFlag(int t){
		check_timer[t] = CHECK_INTERVAL;
	}
	bool isCheckable(int t){
		return check_timer[t] < 1;
	}
	
	void move()
	{
		if(chiptype == CHIPTYPE.NONE) return;
		timer++;
		
		foreach(inout t;check_timer) if(t > 0) t--;
		
		// ライフ減少
		bool isFlash = false;
		foreach(inout t;check_timer) if(t > 0) isFlash = true;
		
		double declife = LIFE_TIMEDEC;
		if(!isFlash) declife *= LIFE_FLASH_DECRATIO;
		life -= declife;
		
		if(life < 0) chiptype = CHIPTYPE.NONE;
	}
	
	void drawBox()
	{
		if(chiptype == CHIPTYPE.NONE) return;
		
		double alpha = 1;
		if(life < LIFE_MAX / 5.0) alpha = 0.7;
		
		glPushMatrix();
			{
			glTranslatef(0,0,GRID_SIZE * life / LIFE_MAX + GRID_SIZE);
			glScalef(GRID_SIZE , GRID_SIZE , GRID_SIZE * life / LIFE_MAX);
			glColor4f(player_color_r[ cast(int)chiptype ] ,
					player_color_g[ cast(int)chiptype ] ,
					player_color_b[ cast(int)chiptype ] , 0.5 * alpha);
			CubeShape.drawPolygon();
			glColor4f(player_color_r[ cast(int)chiptype ] ,
					player_color_g[ cast(int)chiptype ] ,
					player_color_b[ cast(int)chiptype ] , 0.5 * alpha);
			//glColor4f(1,1,1 , 1);
			//CubeShape.drawWire();
		}
		glPopMatrix();
	}
	
	void drawMark()
	{
		// マーク
		glPushMatrix();
		{
			bool isFlash = false;
			foreach(inout t;check_timer) if(t > 0) isFlash = true;
			
			glTranslatef(0,0,GRID_SIZE * life / LIFE_MAX * 2 + GRID_SIZE);
			glScalef(GRID_SIZE * 2 , GRID_SIZE * 2 , 0);
			
			if(!isFlash || timer % 4 < 2){
				glColor4f(0.5 , 0.5 , 0.5 , 0.7);
				Hell_setAlpha(HELL_ALPHA_NORMAL);
			}else{
				glColor4f(1 , 1 , 1 , 1);
				Hell_setAlpha(HELL_ALPHA_ADD);
			}
//			if(!isFlash || timer % 4 < 2){
				switch(chiptype){
					case CHIPTYPE.CIRCLE:
						Hell_drawBillBoard("mark" , 0,0,12,12);
						break;
					case CHIPTYPE.CROSS:
						Hell_drawBillBoard("mark" , 12,0,12,12);
						break;
					default:
						break;
				}
				Hell_setAlpha(HELL_ALPHA_NORMAL);
//			}
		}
		glPopMatrix();
	}
}

//--------------------------------------------------------------------

enum GAMEMODE{
	NORMAL,
	MAX,
}
enum GAMESTATE{
	BEGIN,
	PLAYING,
	END,
	OVER,
}

class GameCore{
	Player[] player = null;
	GAMEMODE gamemode;
	GAMESTATE gamestate;
	Map map = null;
	
	int countdown_timer;
	int over_timer;
	int timer = 0;
	int goalscore = 5000;
	
	int winner = 0;
	
	Keypad[] keypad;
	
	this(GAMEMODE gm)
	{
		gamemode = gm;
		gamestate = GAMESTATE.BEGIN;
		map = new Map(this);
		player = new Player[PLAYER_MAX];
		countdown_timer = 60 * 3;
		over_timer = 0;
		timer = 0;
		
		keypad = new Keypad[2];
		keypad[0] = new Keypad(0);
		keypad[1] = new Keypad(1);
		player[0] = new Player(CHIPTYPE.CIRCLE , this , keypad[0]);
		player[1] = new Player(CHIPTYPE.CROSS , this , keypad[1]);
	}
	
	void move()
	{
		timer++;
		if(countdown_timer > -120) countdown_timer--;
		if(gamestate == GAMESTATE.BEGIN){
			if(countdown_timer < 0) gamestate = GAMESTATE.PLAYING;
		}
		
		if(gamestate == GAMESTATE.END){
			over_timer++;
			if(over_timer > 30 && (keypad[0].isPushEnter() || keypad[1].isPushEnter())) gamestate = GAMESTATE.OVER;
		}
		
		map.move();
		foreach(inout p;player){
			p.move();
			
			if(gamestate == GAMESTATE.PLAYING){
				if(p.score > goalscore){
					gamestate = GAMESTATE.END;
					winner = p.chiptype;
				}
			}
		}
	}
	
	void draw()
	{
		for(int x=-5;x<6;x++){
			for(int y=-5;y<6;y++){
				double hi = CHAR_Z_SURFACE * 2;
				if(y > -3 && y < 3 && x > -3 && x < 3) hi /= 10;
					glPushMatrix();
					glTranslatef(x * 15 , y * 15 , CHAR_Z_SURFACE * 3);
					glRotatef(0,0,timer);
			//		glTranslatef(0,0,CHAR_Z_SURFACE * 1);
					glScalef(2,2,hi);
					glColor4f(0.2 , 0.2 , 0.2 , 0.5);
					CubeShape.drawPolygon();
			//		CubeShape.drawWire();
					glPopMatrix();
			}
		}
		
		
		map.draw();
		Hell_setAlpha(HELL_ALPHA_ADD);
		foreach(inout p;player) p.draw();
		Hell_setAlpha(HELL_ALPHA_NORMAL);
		
		resetPerspective();
		foreach(inout p;player) p.drawStatus();
		map.drawStatus();
		
		if(countdown_timer > -60){
			if(countdown_timer > 0){
				Hell_drawFont(std.string.toString((countdown_timer / 6) / 10.0) , 280,200,5);
			}else{
				Hell_drawFont("START" , 280,200,3);
			}
		}
		
		if(gamestate == GAMESTATE.END){
			if(winner == CHIPTYPE.CIRCLE)
				Hell_drawFont("1P WIN" , 280,200,3);
			if(winner == CHIPTYPE.CROSS)
				Hell_drawFont("2P WIN" , 280,200,3);
		}
		setPerspective();
	}
	
	bool isOver(){
		return (gamestate == GAMESTATE.OVER);
	}
}